--Can I just say "fuck you Garry"?
--Thanks.

AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_mshud.lua" )
AddCSLuaFile( "cl_ms_scoreboard.lua" )
AddCSLuaFile( "cl_ms_pickteam.lua" )
AddCSLuaFile( "shared.lua" )

include( "sv_strikemods.lua" )
include( "sv_roundcontroller.lua" )
include( "shared.lua" )
include( "tags.lua" )

util.PrecacheSound("metastrike/misc/rules_update.wav")
util.PrecacheSound("metastrike/misc/weapons_issue.wav")
util.PrecacheSound("metastrike/misc/diseasecough01.wav")
util.PrecacheSound("metastrike/misc/diseasecough02.wav")
util.PrecacheSound("metastrike/misc/diseasecough03.wav")
util.PrecacheSound("metastrike/misc/diseasecough04.wav")
util.PrecacheSound("metastrike/misc/ally_die.wav")
util.PrecacheSound("metastrike/misc/enemy_die.wav")
util.PrecacheSound("metastrike/misc/miss.wav")
util.PrecacheSound("metastrike/misc/mingebags_victory.wav")
util.PrecacheSound("metastrike/misc/admins_victory.wav")
util.PrecacheSound("metastrike/misc/1min_warning.wav")
util.PrecacheSound("metastrike/misc/cntdn_3.wav")
util.PrecacheSound("metastrike/misc/cntdn_2.wav")
util.PrecacheSound("metastrike/misc/cntdn_1.wav")
util.PrecacheSound("metastrike/misc/cntdn_0.wav")
util.PrecacheSound("metastrike/misc/draw.wav")
util.PrecacheSound("metastrike/misc/timeover.wav")

RSTATE_WAITFORPLS 	= 0
RSTATE_STARTGAME 	= 1
RSTATE_INROUND 		= 2
RSTATE_ENDROUND 	= 3
RSTATE_MAPCYCLE 	= -1

EnglishDeathEnts = {
trigger_hurt = "Map Hazard",
point_hurt = "Point Hazard",
prop_physics = "Something Heavy",
prop_physics_multiplayer = "Something Heavy",
env_beam = "Lazorz",
info_dummy_fire = "Combustion"
}

EnglishDeathTypes = {}
EnglishDeathTypes[DMG_VEHICLE] = "coughed up a lung"
EnglishDeathTypes[DMG_DROWN] = "slept with the fishes"
EnglishDeathTypes[DMG_CRUSH] = "became a gorey pancake"
EnglishDeathTypes[DMG_FALL] = "hit the ground kinda hard"

function GM:Initialize()
	local GM = GAMEMODE
	GM.round_go=false
	GM.next_disease=0
	MsgN("Metastrike13 beta v1.0 initializing")
	-- Finally, I rid myself of the old mod system and use something a bit more efficient.
	SetGlobalString("ms_mod1","---") -- That's right, fuck VARule01-03! We do our own shit here, nigga!
	SetGlobalString("ms_mod2","---")
	SetGlobalString("ms_mod3","---")
	
	SetGlobalInt("ms_rndstate",0) -- 0 = Waiting for players, 1 = Round starting (countdown), 2 = Playing, 3 = Round end
	SetGlobalFloat("ms_rndtimer",0)
	SetGlobalInt("ms_rndresult",0)
	
	GM.nextmap_timerstart = CurTime()
	GM.nextmapis = "Unknown"
	
	SetGlobalInt("ms_maxhp",100)
	
	   local cstrike = false
   for _, g in pairs(engine.GetGames()) do
      if g.folder == 'cstrike' then cstrike = true end
   end
   if not cstrike then
      ErrorNoHalt("Meta-Strike detects that you don't have Counter-Strike Source. Shit may break. May want to fix that.\n")
   end
   GM.disease = nil
end

function GM:Think()
	GM=GAMEMODE
	GM:RoundController()
	if CurTime()>GM.next_disease then
		GM.next_disease = CurTime() + 1
		if (GM:MSCheckForMod("Deadly Disease") or GM:MSCheckForMod("AIDS")) and GetGlobalInt("ms_rndstate", 0)==RSTATE_INROUND and CurTime()>GetGlobalFloat("ms_rndtimer", 0) then
			local playingplayers = {}
			table.Add(playingplayers, team.GetPlayers(TEAM_MBAGS))
			table.Add(playingplayers, team.GetPlayers(TEAM_ADMIN))
			local diseasedmg = DamageInfo()
			diseasedmg:SetAttacker(ents.FindByClass("info_dummy_deadlydisease")[1])
			--diseasedmg:SetInflictor(GM.disease)
			diseasedmg:SetDamage(1)
			diseasedmg:SetDamageType(DMG_VEHICLE)
			for _,p in pairs(playingplayers) do
				if p:Alive() then
					p:TakeDamageInfo(diseasedmg)
					if math.random(1,6)<5 then p:EmitSound(Sound("metastrike/misc/diseasecough0"..math.random(1,4)..".wav")) end
				end
			end
		end
	end
	for k,v in pairs(player.GetBots()) do
		if v:Team()==TEAM_UNASSIGNED then
			local mb = #team.GetPlayers(TEAM_MBAGS)
			local ad = #team.GetPlayers(TEAM_ADMIN)
			if mb<ad then
				v:SetTeam(TEAM_MBAGS)
				MsgN("Assigned bot "..v:Name().." to Mingebags")
			elseif ad<mb then
				v:SetTeam(TEAM_ADMIN)
				MsgN("Assigned bot "..v:Name().." to Administration")
			elseif ad==mb then
				local nT = table.Random({TEAM_ADMIN,TEAM_MBAGS})
				v:SetTeam(nT)
				MsgN("Assigned bot "..v:Name().." to "..team.GetName(nT))
			end
		end
	end
	for k,v in pairs(team.GetPlayers(TEAM_SPECTATOR)) do
		if v:Alive() then GAMEMODE:PlayerDeathThink( v ) end
	end
	for k,v in pairs(GM:GetPlayingPlayers()) do
		if (v.OnFireTime or 0)>CurTime() then
			if (v.NextFireTime or 0)<CurTime() then
				v.NextFireTime=CurTime()+0.5
				local diseasedmg = DamageInfo()
				diseasedmg:SetAttacker(v.OnFireAttacker or ents.FindByClass("info_dummy_fire")[1])
				diseasedmg:SetInflictor(ents.FindByClass("info_dummy_fire")[1])
				--diseasedmg:SetInflictor(GM.disease)
				diseasedmg:SetDamage(1)
				diseasedmg:SetDamageType(DMG_BURN)
				v:TakeDamageInfo(diseasedmg)
			end
		end
	end
end

function GM:PlayerDeath( pl, inflictor, attacker )
	GAMEMODE:MSPlayerDeath(pl, inflictor, attacker, false)
end

function GM:PlayerSilentDeath( pl, inflictor, attacker )
	GAMEMODE:MSPlayerDeath(pl, inflictor, attacker, true)
end

function GM:MSPlayerDeath(pl, inflictor, attacker, silent) -- Centralizing PlayerDeath and PlayerSilentDeath
	local rndstate = GetGlobalInt("ms_rndstate", 0)
	if rndstate==2 and !silent then
		GAMEMODE:PlaySoundToTeam("metastrike/misc/ally_die.wav",pl:Team())
		local enemy=0
		if pl:Team()==TEAM_MBAGS then enemy=TEAM_ADMIN else enemy=TEAM_MBAGS end
		GAMEMODE:PlaySoundToTeam("metastrike/misc/enemy_die.wav",enemy)
		if GAMEMODE:MSCheckForMod("Explode on Death") then
			timer.Simple(0.5, function()
			local radius = 300
			
			util.BlastDamage( pl, pl, pl:GetPos(), radius, 200 )
			
			local effectdata = EffectData()
			 effectdata:SetOrigin( pl:GetPos() )
			util.Effect( "Explosion", effectdata, true, true )
			end)
		end
		if !GAMEMODE:MSCheckForMod("No Ammo Drops") then
			local drop = ents.Create("item_msw_ammo")
			local mul = 2
			if GAMEMODE:MSCheckForMod("More Ammo Dropped") then mul = 1 end
			drop.PAmmo = math.floor(pl:GetAmmoCount("pistol")/mul)
			drop.SAmmo = math.floor(pl:GetAmmoCount("ar2")/mul)
			drop:SetPos(pl:GetPos())
			drop:Spawn()
		end
	end
	if IsValid(attacker) and attacker:IsPlayer() and attacker!=pl then
		timer.Simple(2, function() if !IsValid(pl) or pl:Alive() then return false end if attacker:IsValid() then pl:Spectate(OBS_MODE_FREEZECAM) pl:SpectateEntity(attacker) end end)
	end
	timer.Simple(5, function() if !IsValid(pl) or pl:Alive() then return false end if pl:GetObserverMode()==OBS_MODE_NONE or pl:GetObserverMode()==OBS_MODE_FREEZECAM then pl:Spectate(OBS_MODE_CHASE) pl:SpectateEntity(table.Random(team.GetPlayers(pl:Team()))) end end)
	if !silent then
		---[[
		if ( !IsValid( inflictor ) && IsValid( attacker ) ) then
			inflictor = attacker
		end
		if ( inflictor && inflictor == attacker && inflictor:IsPlayer() ) then
			inflictor = inflictor:GetActiveWeapon()
			if ( !IsValid( inflictor ) ) then inflictor = attacker end
		end
		--]]
		umsg.Start( "MSNotice" )
			if attacker:IsValid() and attacker:IsPlayer() then
			umsg.String( "PlayerKilledPlayer" )
			umsg.Entity( attacker )
			umsg.String( inflictor:GetClass() )
			umsg.Entity( pl )
			end
			if !attacker:IsValid() then
				if attacker==game.GetWorld() and pl.LastDamageType and EnglishDeathTypes[pl.LastDamageType] then
					umsg.String( "PlayerSpecialAction" )
					umsg.Entity( pl )
					umsg.String( EnglishDeathTypes[pl.LastDamageType] or "tripped on a stick or something" )
				else
				umsg.String( "SpecialKilledPlayer" )
				umsg.String( "Nothing" )
				umsg.Entity( pl )
				end 
			end
			if attacker:IsValid() and !attacker:IsPlayer() then
				if pl.LastDamageType and EnglishDeathTypes[pl.LastDamageType] then
					umsg.String( "PlayerSpecialAction" )
					umsg.Entity( pl )
					umsg.String( EnglishDeathTypes[pl.LastDamageType] )
				else
					umsg.String( "SpecialKilledPlayer" )
					umsg.String( EnglishDeathEnts[attacker:GetClass()] or attacker:GetClass() )
					umsg.Entity( pl )
				end
			end
		umsg.End()
	end
	pl.TimeofDeath = CurTime()
	
	-- Mid-round team auto-balance
	if pl:Team()==TEAM_MBAGS or pl:Team()==TEAM_ADMIN then
		local enemy=0
		if pl:Team()==TEAM_MBAGS then enemy=TEAM_ADMIN else enemy=TEAM_MBAGS end
		if #team.GetPlayers(pl:Team())-#team.GetPlayers(enemy)>1 then
			pl:SetTeam(enemy)
			for k,v in pairs( player.GetAll() ) do
				v:ChatPrint( pl:Name().." was moved to team "..team.GetName(enemy).." for balance" )
			end
		end
	end
end

function GM:PlayerDeathThink( pl )
	-- prevent respawning
	if pl:GetObserverMode()==OBS_MODE_FIXED then pl:SetObserverMode(OBS_MODE_CHASE) end
	if (pl:GetObserverMode()==OBS_MODE_CHASE or pl:GetObserverMode()==OBS_MODE_IN_EYE) then
		if pl:KeyPressed(IN_ATTACK) then
			local teamcycle = {}
			if pl:Team()==TEAM_ADMIN or pl:Team()==TEAM_MBAGS or pl:Team()==TEAM_HAZARD then
				for k,v in pairs(team.GetPlayers(pl:Team())) do
					if v:Alive() then 
						table.insert(teamcycle,v)
					end
				end
			else
				for k,v in pairs(player.GetAll()) do
					if v:Alive() and (v:Team()==TEAM_ADMIN or v:Team()==TEAM_MBAGS or v:Team()==TEAM_HAZARD) then 
						table.insert(teamcycle,v)
					end
				end
			end
			if !IsValid(pl:GetObserverTarget()) or !pl:GetObserverTarget():Alive() or !table.HasValue(teamcycle, pl:GetObserverTarget()) then
				if #teamcycle>0 then
					pl:SpectateEntity(table.Random(teamcycle))
				end
			else
				local found = false
				for numb,spl in pairs(teamcycle) do
					if !found then
					if pl:GetObserverTarget()==spl then
						found=true
						if numb==#teamcycle then 
							pl:SpectateEntity(teamcycle[1])
						else
							pl:SpectateEntity(teamcycle[numb+1])
						end
					end
					end
				end
			end
		end
		
		if pl:KeyPressed(IN_ATTACK2) then
			local teamcycle = {}
			if pl:Team()==TEAM_ADMIN or pl:Team()==TEAM_MBAGS or pl:Team()==TEAM_HAZARD then
				for k,v in pairs(team.GetPlayers(pl:Team())) do
					if v:Alive() then 
						table.insert(teamcycle,v)
					end
				end
			else
				for k,v in pairs(player.GetAll()) do
					if v:Alive() and (v:Team()==TEAM_ADMIN or v:Team()==TEAM_MBAGS or v:Team()==TEAM_HAZARD) then 
						table.insert(teamcycle,v)
					end
				end
			end
			if !pl:GetObserverTarget():Alive() or !table.HasValue(teamcycle, pl:GetObserverTarget()) then
				if #teamcycle>0 then
					pl:SpectateEntity(table.Random(teamcycle))
				end
			else
				local found = false
				for numb,spl in pairs(teamcycle) do
					if !found then
					if pl:GetObserverTarget()==spl then
						found=true
						if numb==1 then 
							pl:SpectateEntity(teamcycle[#teamcycle])
						else
							pl:SpectateEntity(teamcycle[numb-1])
						end
					end
					end
				end
			end
		end
		
		if pl:KeyPressed(IN_DUCK) then
			if pl:GetObserverMode()==OBS_MODE_CHASE then
				pl:Spectate(OBS_MODE_IN_EYE)
			else
				pl:Spectate(OBS_MODE_CHASE)
			end
		end
	end
	
	local obent = pl:GetObserverTarget()
	if IsValid(obent) then
		pl:SetPos(obent:GetPos())
	end
end

function GM:PlayerDeathSound()
	-- prevent beebeep beebeep beep beeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefuckyouhl2
	-- dat shit was getting annoying when i went through old ms footage
	return true
end

function GM:PlayerCanJoinTeam( ply, teamid )
	
	local TimeBetweenSwitches = GAMEMODE.SecondsBetweenTeamSwitches or 10
	if ( ply.LastTeamSwitch && RealTime()-ply.LastTeamSwitch < TimeBetweenSwitches ) then
		ply.LastTeamSwitch = ply.LastTeamSwitch + 1;
		ply:ChatPrint( Format( "Please wait %i more seconds before trying to change team again", (TimeBetweenSwitches - (RealTime()-ply.LastTeamSwitch)) + 1 ) )
		return false
	end
	
	-- Already on this team!
	if ( ply:Team() == teamid ) then 
		ply:ChatPrint( "You're already on that team" )
		return false
	end
	
	-- Player balancing
	local pls={
	#team.GetPlayers(TEAM_ADMIN),
	#team.GetPlayers(TEAM_MBAGS)
	}
	if ply:Team()==TEAM_ADMIN or ply:Team()==TEAM_MBAGS then
		pls[ply:Team()] = pls[ply:Team()] - 1
	end
	
	if teamid==TEAM_ADMIN and pls[TEAM_ADMIN]>pls[TEAM_MBAGS] then
		ply:ChatPrint( "Not enough players on Mingebags for you to join Administrators." )
		return false
	end
	if teamid==TEAM_MBAGS and pls[TEAM_MBAGS]>pls[TEAM_ADMIN] then
		ply:ChatPrint( "Not enough players on Administrators for you to join Mingebags." )
		return false
	end
	
	for k, aply in pairs( player.GetAll() ) do
		aply:ChatPrint( ply:Name().." joined team "..team.GetName(teamid) )
	end
	
	return true
	
end

function GM:EntityTakeDamage( target, dmginfo )
	local attacker = dmginfo:GetAttacker()
	if ( target:IsPlayer() and attacker:IsPlayer() and attacker~=target and attacker:Alive() ) then
		if attacker:Team()==target:Team() and GetGlobalInt("ms_rndstate",0)==2 then
			dmginfo:ScaleDamage( 0 )// Damage is now half of what you would normally take.
		end
	end
	target.LastDamageType = dmginfo:GetDamageType()
	return dmginfo
end

function GM:OnPlayerChangedTeam( ply, oldteam, newteam )
	if (oldteam == TEAM_ADMIN or oldteam == TEAM_MBAGS) and ply:Alive() and ply:GetObserverMode(OBS_MODE_NONE) then -- If they were previously playing then kill the shit out of them.
		ply:Kill()
	end
	if newteam==TEAM_SPECTATOR then
		ply:Spectate(OBS_MODE_CHASE)
	end
	
	umsg.Start( "RefreshScoreboard" )
	umsg.End()
end

function GM:PlayerDisconnected( ply )
    umsg.Start( "RefreshScoreboard" )
	umsg.End()
end

function GM:GetFallDamage( ply, flFallSpeed )
	local rtn = flFallSpeed / 16
	if self:MSCheckForMod("No Fall Damage") then rtn = 0 end
	if self:MSCheckForMod("Instakill Fall Damage") then rtn = 9001 end
	return rtn;
end

function GM:PlaySoundToEverybody( snd )
	BroadcastLua("surface.PlaySound('"..snd.."')")
end

function GM:GetPlayingPlayers()
	local plyt = {}
	table.Add(plyt,team.GetPlayers(TEAM_ADMIN))
	table.Add(plyt,team.GetPlayers(TEAM_MBAGS))
	return plyt
end

function GM:PlaySoundToTeam( snd, tm )
	for k, v in pairs(team.GetPlayers(tm)) do
		v:SendLua("surface.PlaySound('"..snd.."')")
	end
end

function GM:HazardizePlayer( pl )
	pl.OriginalTeam=pl:Team()
	pl:SetTeam(TEAM_HAZARD)
end